<template>
	<render />
</template>

<script setup lang="tsx">
import { computed, ref, resolveComponent, watch, inject, getCurrentInstance, onMounted, h } from 'vue'
import useOptionsVNode from '@/hooks/pageMaking/useOptionsVNode'
import { getValidProps, capitalize, getFuncFromString, withExtraParams, toCamelCase, isEmpty } from '@/hooks/pageMaking/useUtil';
import type { JSX } from 'vue/jsx-runtime'
import { noLabelComps } from '@/views/view-editor/config/util';
import SvgIcon from '@/components/SvgIcon/index.vue'

interface IProps {
	options: any
	models: any,
	isDragMode?: boolean
}

const props = withDefaults(defineProps<IProps>(), {
	models: {},
	isDragMode: false
})

const emit = defineEmits<{
	(e: 'update:model', data: Record<string, any>): void
}>()

const pmInstance = inject('pmInstance', null)
const compRef = ref(null)

const showLabel = computed(() => !!props.options.label && !noLabelComps.includes(props.options.component))

const events = computed(() => {
	const res: Record<string, any> = {}
	const originalEvents = props.options.events
	if (originalEvents) {
		const exposed = pmInstance ? (pmInstance as any).exposed : null
		Object.keys(originalEvents).forEach((key) => {
			if (originalEvents[key]) {
				res[`on${capitalize(key)}`] = withExtraParams(getFuncFromString(originalEvents[key]), exposed)
			}
		})
	}
	res['onUpdate:modelValue'] = (val: any) => {
		emit('update:model', val)
	}
	return res
})

const slots = computed(() => {
	const _slots: Record<string, any> = {}
	const originalSlots = props.options.slots
	if (originalSlots) {
		const exposed = pmInstance ? (pmInstance as any).exposed : null
		Object.keys(originalSlots).forEach((key) => {
			if (originalSlots[key] && originalSlots[key].includes('return')) {
				_slots[key] = getFuncFromString(originalSlots[key]).bind(null, exposed, h)
			}
		})
	}
	return _slots
})

const rules = computed(() => {
	const _rules: any[] = []
	if (showLabel.value) {
		const { dataModel, label } = props.options
		const originalRules = dataModel?.rules
		if (originalRules) {
			if (originalRules.required) {
				_rules.push({
					required: true,
					trigger: 'change',
					validator: (rule: any, value: any, callback: (arg?: Error) => void) => {
						if (Array.isArray(value) ? value.length === 0 : isEmpty(value)) {
							callback(new Error(`${label}不能为空`))
						} else {
							callback()
						}
					}
				})
			}
			if (originalRules.pattern) {
				const reg = new RegExp(originalRules.pattern)
				_rules.push({
					trigger: 'change',
					validator: (rule: any, value: any, callback: (arg?: Error) => void) => {
						if (reg.test(value)) {
							callback()
						} else {
							callback(new Error(originalRules.patternErrorMsg || `${label}格式不正确`))
						}
					}
				})
			}
			if (originalRules.validFunc && originalRules.validFunc.includes('function')) {
				const exposed = pmInstance ? (pmInstance as any).exposed : null
				_rules.push({
					trigger: 'change',
					validator: withExtraParams(getFuncFromString(originalRules.validFunc), exposed)
				})
			}
		}
	}
	return _rules
})

const optionsVNode = ref<JSX.Element[] | null>(null)

watch(
	() => props.options.props && props.options.props.dataSource,
	async (dataSource) => {
		if (dataSource) {
			optionsVNode.value = await useOptionsVNode(dataSource, props.options.component, pmInstance)
		}
	},
	{
		immediate: true
	}
)

onMounted(async () => {
	const _props = props.options.props
	if (_props && _props.dataSource && _props.dataSource.type === 'component') {
		const instance = getCurrentInstance()
		const globalComponents = Object.keys(instance?.appContext.components || {})
		const value = globalComponents
			.filter(comp => comp !== toCamelCase(props.options.component))
			.map(comp => ({
				option_key: comp,
				option_desc: comp
			}))
		optionsVNode.value = await useOptionsVNode({
			type: 'static',
			value
		} as any, props.options.component, pmInstance)
	}
})

defineExpose({
	compRef
})

const render = () => {
	const { label, component, props: compProps, model } = props.options

	const { _className, _style, _display = true, ...residueProps } = compProps || {}

	if (!_display) {
		return props.isDragMode
			? (
				<div class="page-making-item-hidden">
					<SvgIcon iconName='icon-eye-close'></SvgIcon>
					<span style='margin-left: 4px;'>不可见</span>
				</div>
			)
			: null
	}

	const comp = resolveComponent(toCamelCase(component))
	const validProps = getValidProps(residueProps, component)
	const dynamicComp = (
		<comp {...validProps} modelValue={props.models[model]} {...events.value} v-slots={slots.value} ref={compRef}>
			{optionsVNode.value}
			{ toCamelCase(component) === 'eButton' && validProps.text }
		</comp>
	)
	if (showLabel.value) {
		const formItemProps = {
			label,
			prop: model,
			rules: rules.value
		}
		return <eFormItem id={`item_${model}`} class={_className} style={_style} {...formItemProps}>{dynamicComp}</eFormItem>
	}
	return <div id={`item_${model}`} class={_className} style={_style}>{dynamicComp}</div>
}
</script>

<style lang="less" scoped>
	.page-making-item-hidden {
    height: 36px;
    display: flex;
		align-items: center;
		justify-content: center;
		background-color: #f0f0f0;
	}
</style>
